package graphe
{
	import de.polygonal.ds.*;
	
	import flash.display.*;
	import flash.events.*;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.text.*;
	import flash.utils.*;
	
	import mx.collections.ArrayCollection;
	import mx.containers.Canvas;
	import mx.containers.TitleWindow;
	import mx.controls.Alert;
	import mx.controls.ComboBox;
	import mx.controls.Label;
	import mx.controls.TextArea;
	import mx.core.UIComponent;
	import mx.events.CloseEvent;
	import mx.managers.PopUpManager;
	
	
	public class Graphe extends Canvas
	{
		public var graph:Graph;
		private var nodeCount:int = 0;
		private var nodeQue:LinkedQueue;
		private var nodeCanvas:Canvas;
		private var pos:Array = [];
		private var arc:Array = [];
		public var tabNodes:ArrayCollection = new ArrayCollection();
		
		public function Graphe()
		{
			height = 700;
			width = 700;
			
			graph = new Graph(20);
			nodeQue = new LinkedQueue();
			
			addChild(nodeCanvas = new Canvas);
		}
	
		public function reInit():void
		{
			tabNodes.removeAll();
			graph.clear();
			nodeQue.clear();
			nodeCount = 0;
			
			graphics.clear();
			removeChild(nodeCanvas);
			addChild(nodeCanvas = new Canvas());
				
		}
		private function animate(e:TimerEvent):void
		{
			if (nodeQue.size > 0)
			{
				var gn:GraphNode = nodeQue.dequeue();
				
				var node:Node = gn.data;
				node.mark();
				
				drawNodeArcs(gn);
			}
		}
		
		public function makeLinks():void
		{
			for (var i:int = 0; i< tabNodes.length; i++)
			{
				for (var j:int = 0; j< tabNodes.length; j++)
				{
					if (tabNodes[i].color == tabNodes[j].color)
					{
						if (tabNodes[i].color != 0xffff00)
						{
							graph.addArc(tabNodes[i].graphIndex, tabNodes[j].graphIndex);
							arc.push([tabNodes[i].graphIndex, tabNodes[j].graphIndex]);
						}
					}
				}
			}
			draw();
			
		}
		
		public function addNode(node:Node,Ix:int,Iy:int):void
		{
			if (nodeCount > graph.maxSize) return;
			
			//create a node sprite
			nodeCanvas.addChild(node);
			node.x = Ix;
			node.y = Iy;
			
			pos.push([node.x, node.y]);
			tabNodes.addItem(node);
			
			//store it inside the graph at index nodeCount
			graph.addNode(node, node.graphIndex);
			nodeCount++;
			
			//just to make sure nodes don't overlap
			var t:Number = getTimer();
			var overlaps:Boolean = false;
			do
			{
				overlaps = spread();
				
				//bail out
				if (getTimer() - t > 100) break;
			}
			while (overlaps)
			
			//draw all arcs by calling the breadthFirst algorithm on each node
			draw();
		}
		
		private function draw():void
		{
			graphics.clear();
			
			//draw all arcs originating from the given node
			for (var i:int = 0; i < graph.nodes.length; i++)
			{	
				if (graph.nodes[i]) 
				{
					var n:Node = graph.nodes[i].data;
					graphics.lineStyle(2, n.color, 1);
					graph.breadthFirst(graph.nodes[i], drawNodeArcs);
				}
			}
		}
		
		private function drawNodeArcs(node:GraphNode):void
		{
			var arcStart:Node = node.data;
			
			for (var i:int = 0; i < node.numArcs; i++)
			{
				var arc:GraphArc = node.arcs[i];
				var arcTarget:Node = arc.node.data;
				
				var dx:Number = arcTarget.x - arcStart.x;
				var dy:Number = arcTarget.y - arcStart.y;
				var len:Number = Math.sqrt(dx * dx + dy * dy);
				
				var xdir:Number = dx / len;
				var ydir:Number = dy / len;
				
				drawArrow(graphics, arcStart.x + xdir * (Node.RADIUS + 4), arcStart.y + ydir * (Node.RADIUS + 4),
					xdir, ydir, len - 2 * (Node.RADIUS + 4), 4);
			}
		}
		
		private function freezeNodes(flag:Boolean):void
		{
			//enable/disable mouse events for all nodes
			for (var i:int = 0; i < nodeCanvas.numChildren; i++)
			{
				var node:Node = nodeCanvas.getChildAt(i) as Node;
				node.freeze(flag);
			}
		}
		
		private function unmarkNodes():void
		{
			//reset all nodes
			for (var i:int = 0; i < nodeCanvas.numChildren; i++)
			{
				var node:Node = nodeCanvas.getChildAt(i) as Node;
				node.unmark();
			}
		}
		
		private function spread():Boolean
		{
			var overlaps:Boolean = false;
			for (var i:int = 0; i < nodeCanvas.numChildren - 1; i++)
			{
				var nodeA:Node = nodeCanvas.getChildAt(i) as Node;
				
				for (var j:int = i + 1; j < nodeCanvas.numChildren; j++)
				{
					var nodeB:Node = nodeCanvas.getChildAt(j) as Node;
					
					var dx:Number = nodeA.x - nodeB.x + .1;
					var dy:Number = nodeA.y - nodeB.y + .1;
					if ((dx * dx + dy * dy) <= (3 * Node.RADIUS) * (3 * Node.RADIUS))
					{
						var dist:Number = Math.sqrt(dx * dx + dy * dy);
						var xdir:Number = dx / dist;
						var ydir:Number = dy / dist;
						nodeA.x += xdir * (3 * Node.RADIUS - dist);
						nodeA.y += ydir * (3 * Node.RADIUS - dist);
						overlaps = true;
					}
				}
			}
			return overlaps;
		}
		
		private function drawArrow(g:Graphics, x:Number, y:Number, xdir:Number, ydir:Number, len:Number, size:Number)
		{
			
			var t:Number = 1 / Math.sqrt(xdir * xdir + ydir * ydir);
			
			var ex:Number = (xdir * t * len) + x;
			var ey:Number = (ydir * t * len) + y;
			
			g.moveTo(x, y);
			g.lineTo(ex, ey);
			
			var dx:Number = (ex - x);
			var dy:Number = (ey - y);
			
			var l:Number = Math.sqrt((dx * dx) + (dy * dy));
			if (l <= 0)	return;
			
			dx /= l;
			dy /= l;
			var nx:Number = -dy;
			var ny:Number =  dx;
			g.moveTo(ex, ey);
			g.lineTo((ex - (size * dx)) - (size * nx), (ey - (size * dy)) - (size * ny));
			g.moveTo(ex, ey);
			g.lineTo((ex - (size * dx)) + (size * nx), (ey - (size * dy)) + (size * ny));
		}
	}
}
